import pandas as pd
import numpy as np
from sklearn import preprocessing
import matplotlib.pyplot as plt
from sklearn.decomposition import PCA
from sklearn.linear_model import LinearRegression
from sklearn.preprocessing import StandardScaler
from openpyxl import load_workbook
import os

os.chdir(os.path.dirname(__file__))

# 读取文件
data = pd.read_excel(".\\month_index.xlsx", skiprows=1)
# data = data.iloc[:,1:16]

# 过滤数值列
data_numeric = data.select_dtypes(include=['number'])
# 如果没有数值列，尝试将所有列强制转换为数值
if data_numeric.empty:
    print("输入表格中无数值列，将把所有列值转换为数值")
    data_numeric = data.apply(pd.to_numeric, errors='coerce')
# 检查空值并填充
if data_numeric.isnull().values.any():
    data_numeric = data_numeric.fillna(0)
# 检查是否为空
if data_numeric.empty:
    raise ValueError("输入表格为空")
else:
    # 标准化
    zscore = StandardScaler()
    data_zs = zscore.fit_transform(data_numeric)
# 将其转换为数据框
newdata = pd.DataFrame(data_zs)
pca = PCA()
pca.fit(newdata)
# 生成Excel表格记录成分方差贡献率
pca_ss_variance = pca.explained_variance_
pca_ss_variance = pd.DataFrame(pca_ss_variance)
# 标准化方差贡献率
pca_ss_variance[0] = pca_ss_variance[0] * 10
pca_ss_variance = pca_ss_variance.round(2)
# 转置数组以匹配Excel表格
pca_ss_variance = pca_ss_variance.T
# 标题绘制
pca_ss_variance.columns = [f'成分{i}(%)' for i in range(1,9)]
# 作表
result_table_file = 'source_analysis_result.xlsx'
pca_ss_variance.to_excel(result_table_file, sheet_name='Sheet1', index=False, header=True)  # header=True表示保存列标题

n_components = 0

while n_components < 8:
    n_components += 1
    # 进行主成分分析
    pca = PCA()
    pca.fit(newdata)
    pca_selected = PCA(n_components=n_components)
    data_transformed = pca_selected.fit_transform(data_zs)
    # 显示主成分数确定后的结果
    # print(f"已提取前 {n_components} 个主成分。")
    # 计算各主成分得分
    data_new = pca_selected.transform(data_zs)  # 使用提取的主成分对数据进行变换
    # 自动生成列名，例如 成分1, 成分2, ..., 成分n
    columns = [f"成分{i + 1}" for i in range(n_components)]
    # 将得分结果存储为 DataFrame
    score = pd.DataFrame(data_new, columns=columns)

    from factor_analyzer import FactorAnalyzer

    # 检查数据有效性
    # print("检查数据是否存在 NaN 或 inf 值...")
    if newdata.isnull().values.any():
        # print("检测到 NaN 值，正在填充...")
        newdata = newdata.fillna(0)  # 填充 NaN 值为 0

    if (newdata == float('inf')).values.any() or (newdata == -float('inf')).values.any():
        # print("检测到 inf 值，正在替换...")
        newdata = newdata.replace([float('inf'), -float('inf')], 0)  # 替换 inf 值为 0

    # 确保数据为数值型
    # print("过滤非数值列...")
    newdata = newdata.select_dtypes(include=[np.number])

    # 确保数据中不存在标准差为 0 的列（无法标准化）
    # print("检查是否存在标准差为 0 的列...")
    zero_std_cols = newdata.columns[newdata.std(axis=0) == 0]
    if len(zero_std_cols) > 0:
        # print(f"检测到标准差为 0 的列：{zero_std_cols}")
        newdata = newdata.drop(columns=zero_std_cols)  # 删除这些列

    # 进行因子数确定
    fa = FactorAnalyzer(
        n_factors=n_components,  # 使用之前定义的主成分数作为因子数
        rotation='varimax',  # 选择旋转方法
        method='principal',  # 使用主成分法
        use_smc=True,  # 使用共同度分析
    )
    fa.fit(newdata)  # 适配数据

    # 得到APCS数据框
    newdata_APCS = pd.DataFrame(data_zs)
    new_row = pd.Series([0] * len(newdata_APCS.columns), index=newdata_APCS.columns)
    newdata_APCS = newdata_APCS._append(new_row, ignore_index=True)
    # 进行APCS主成分分析
    pca = PCA()
    pca.fit(newdata_APCS)

    pca_selected = PCA(n_components=n_components)
    data_transformed_APCS = pca_selected.fit_transform(data_zs)
    # 显示主成分数确定后的结果

    data_new_APCS = pca_selected.transform(data_zs)  # 使用提取的主成分对数据进行变换
    # 自动生成列名，例如 成分1, 成分2, ..., 成分n
    columns = [f"成分{i + 1}" for i in range(n_components)]
    # 将得分结果存储为 DataFrame
    score_APCS = pd.DataFrame(data_new_APCS, columns=columns)

    # 因子分析
    # 导入因子分析库
    from factor_analyzer import FactorAnalyzer

    # 检查数据有效性
    # print("检查数据是否存在 NaN 或 inf 值...")
    if newdata_APCS.isnull().values.any():
        # print("检测到 NaN 值，正在填充...")
        newdata_APCS = newdata_APCS.fillna(0)  # 填充 NaN 值为 0

    if (newdata_APCS == float('inf')).values.any() or (newdata_APCS == -float('inf')).values.any():
        # print("检测到 inf 值，正在替换...")
        newdata_APCS = newdata_APCS.replace([float('inf'), -float('inf')], 0)  # 替换 inf 值为 0

    # 确保数据为数值型
    # print("过滤非数值列...")
    newdata_APCS = newdata_APCS.select_dtypes(include=[np.number])

    # 确保数据中不存在标准差为 0 的列（无法标准化）
    # print("检查是否存在标准差为 0 的列...")
    zero_std_cols_APCS = newdata_APCS.columns[newdata_APCS.std(axis=0) == 0]
    if len(zero_std_cols_APCS) > 0:
        # print(f"检测到标准差为 0 的列：{zero_std_cols_APCS}")
        newdata_APCS = newdata_APCS.drop(columns=zero_std_cols_APCS)  # 删除这些列

    # 进行因子数确定
    fa = FactorAnalyzer(
        n_factors=n_components,  # 使用之前定义的主成分数作为因子数
        rotation='varimax',  # 选择旋转方法
        method='principal',  # 使用主成分法
        use_smc=True,  # 使用共同度分析
    )
    fa.fit(newdata_APCS)  # 适配数据

    # # 结果展示
    # print('因子载荷矩阵：',fa.loadings_)
    # print('各变量的共同度',fa.get_communalities())
    # 计算因子得分
    data_new_APCSf = fa.transform(newdata_APCS)
    columns = [f"因子{i + 1}" for i in range(n_components)]  # 动态生成因子列名
    score_APCS_f = pd.DataFrame(data_new_APCSf, columns=columns)

    # 多元线性回归
    regressor = LinearRegression()
    # i是指标的列数,按表格列顺序
    i = 1
    source_point = []
    # 回归分析循环模块
    while i <= 7:

        regressor.fit(score.iloc[1:, :], newdata.iloc[1:, i])  # 使用两个主成分作为自变量进行回归分析
        coefficients = regressor.coef_
        intercept = regressor.intercept_
        # print(coefficients)
        # print(intercept)
        # print(score_APCS_f.iloc[-1:,:])
        # print(float(score_APCS_f.iloc[-1:,1]))
        # 定义APCS源总和初值
        source_sum = abs(intercept)
        source = []
        # APCS_number = []
        # k = 0
        # while k < n_components:
        #     APCS_number.append(float(score_APCS_f.iloc[-1:,k+1]))
        #     k = k + 1
        k = 0
        while k < n_components:
            source_sum = source_sum + abs(float(coefficients[k]) * float(score_APCS.iloc[-1, k]))
            k = k + 1
        # print(source_sum)
        source = []
        k = 0
        while k < n_components:
            source.append(abs(float(coefficients[k]) * float(score_APCS.iloc[-1, k]) / source_sum))
            k = k + 1
        i += 1
        source.append(abs(intercept / source_sum))
        source_int = [f"{x * 100:.2f}" for x in source]
        # 打印各指标成分占比
        # print(source_int)
        source_point.append(source_int)
    # 指定行名称和列名称
    row_names = [
        "pH(/)",
        "DO(mg/L)",
        "CODMn(mg/L)",
        "CODCr(mg/L)",
        "BOD5(mg/L)",
        "NH3-N(mg/L)",
        "TP(mg/L)"
    ]


    # 列名称与用户输入的主成分数相关
    def get_column_names(n):
        return [f"\u6210\u5206{i + 1}" for i in range(n)]


    column_names = get_column_names(n_components)

    # 随机填充一个表格内容用于演示（实际可替换为主成分贡献值或得分值）
    data_for_table = [float(x.strip('%')) / 100 for x in source_int]  # 获取主成分最后一行的值

    # 构建表格
    final_table = pd.DataFrame(
        data=source_point,  # 假设 source_int_results 是一个二维列表，存储了所有回归结果
        columns=[f"成分{i + 1}（%）" for i in range(n_components)] + ["未知成分（%）"],
        # 前 i 个成分命名为“成分1, 成分2, ...”，最后一个为“未知成分”
        index=[
            "pH(无量纲)",
            "溶解氧(mg/L)",
            "高锰酸盐指数(mg/L)",
            "化学需氧量(mg/L)",
            "五日生化需氧量(mg/L)",
            "氨氮(mg/L)",
            "总磷(mg/L)"
        ]  # 固定行名称
    )
    # 保存为 Excel 文件
    with pd.ExcelWriter(result_table_file, engine='openpyxl', mode='a', if_sheet_exists='replace') as writer:
        # 追加工作表到文件中
        final_table.to_excel(writer, sheet_name=f'Sheet{n_components + 1}', index=True, header=True)

